import time

from util import html

_tzOffset = time.altzone if time.daylight else time.timezone

def convertTimestamp(ts):
    """Converts a Unix timestamp to a FLOT timestamp"""
    return 1000*(ts - _tzOffset)

class PlotError(Exception):
    pass

class Enumerated(object):
    """
    Represents a series of values that is automatically enumerated
    """
    def __init__(self,values):
        self.values = values
        
    def __iter__(self):
        return enumerate(self.values)

class PlotFrame(object):
    """
    Represents a plot consisting of one or more series of displayed data
    """
    
    OptionsList = ('legend','xaxis','yaxis','x2axis','y2axis','colors')
    
    def __init__(self,*series,**options):
        self.series = series[:]
        self.options = options
        self.id = options.get('id','plot')
    
    def html(self,className='plot'):
        content = html.Div('',id=self.id,className=className)
        content.append(html.Script(self.jsonString(),
            language="javascript",type="text/javascript"))
        return content
    
    def jsonString(self):
        """
        Returns a JSON string representation of this plot frame suitable for FLOT
        """
        # use a previously cached value if possible
        if hasattr(self,'jsonCache'):
            return self.jsonCache
        self.jsonCache = '$.plot($("#%s"),[\n' % self.id
        for comma,s in enumerate(self.series):
            if comma:
                self.jsonCache += ','
            self.jsonCache += s.jsonString()
        self.jsonCache += '],{'
        separator = ''
        for option in self.options:
            if option in PlotFrame.OptionsList:
                self.jsonCache += '%s%s:%s' % (separator,option,self.options[option])
                separator = ','
        self.jsonCache += '});'
        return self.jsonCache


class PlotSeriesBase(object):
    """
    Represents an (x,y) array of data points suitable for plotting
    """

    OptionsList = ('color','label','lines','bars','points','threshold','xaxis','yaxis',
        'clickable','hoverable','shadowSize')


    def setBoundingBox(self,data):
        """
        Sets our bounding box using the (x,y) iterator provided
        """
        self.bbox = None
        first = True
        for (x,y) in data:
            if first:
                (xmax,ymax) = (xmin,ymin) = (x,y)
                first = False
            else:
                if x < xmin:
                    xmin = x
                elif x > xmax:
                    xmax = x
                if y < ymin:
                    ymin = y
                elif y > ymax:
                    ymax = y
        self.bbox = (xmin,xmax,ymin,ymax)
        self.xRange = xmax-xmin
        self.yRange = ymax-ymin
        
    def jsonString(self):
        """
        Returns a JSON string representation of this series suitable for FLOT
        """
        # use a previously cached value if possible
        if hasattr(self,'jsonCache'):
            return self.jsonCache
        # build and remember the result string
        self.jsonCache = '{data:['
        for comma,(x,y) in enumerate(self.data):
            if comma:
                self.jsonCache += ',\n\t'
            self.jsonCache += '[%f,%f]' % (x,y)
        self.jsonCache += ']\n'
        for option in self.options:
            if option in PlotSeriesBase.OptionsList:
                self.jsonCache += ',%s:%s\n' % (option,self.options[option])
        self.jsonCache += '}\n'
        return self.jsonCache
        
    def scaleY(self,factor=1):
        for index,(x,y) in enumerate(self.data):
            self.data[index] = (x,factor*y)
        
class PlotSeriesRaw(PlotSeriesBase):
    
    def __init__(self,inputData,**options):
        """
        Copies its input data without any processing
        """
        self.options = options
        self.setBoundingBox(inputData)
        self.data = [(x,y) for (x,y) in inputData]
            

class PlotSeriesReduced(PlotSeriesBase):

    def __init__(self,inputData,**options):
        """
        Processes inputData for plotting
        """
        self.options = options
        maxPoints = options.get('maxPoints',50)
        smoothing = options.get('smoothing',0.)
        self.setBoundingBox(inputData)
        self.data = [ ]

        # loop over plotting x intervals
        dx = self.xRange/maxPoints
        window = 0.5*dx*(1+smoothing)
        for index in xrange(maxPoints):
            xmid = self.bbox[0] + (index+0.5)*dx
            # find all input points within this interval
            found = 0
            xsum,ysum = 0.,0.
            for (x,y) in inputData:
                if abs(x-xmid) < window:
                    xsum += x
                    ysum += y
                    found += 1
            if found:
                self.data.append((xsum/found,ysum/found))